package org.manon.json.asm;

import java.io.IOException;
import java.io.OutputStream;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;

import org.manon.json.JSONConstants;
import org.manon.json.io.Serializer;

/**
 * @author <a href="mailto:jacques.militello@gmail.com">Jacques Militello</a>
 */
public abstract class ASMSerializer<T> implements Serializer<T> {

	public static final byte[] SKIP_DOUBLE_QUOTE = new byte[] { 0x5c , 0x22};
	
	public static final byte[] SKIP_LINE_FEED = new byte[]{ 0x5c , 0x6e };
	
	public static final byte[] TRUE = new byte[] { 't' , 'r' , 'u' , 'e'};
	
	public static final byte[] FALSE = new byte[] { 'f' , 'a' , 'l' , 's' , 'e'};
	
	protected boolean writeString(OutputStream os,boolean needComma,byte[] property, boolean value) throws IOException {
		return writeString(os,needComma,property,Boolean.valueOf(value));
	}
	
	protected boolean writeString(OutputStream os,boolean needComma,byte[] property, Boolean value) throws IOException {
		if (value == null) {
			return needComma;
		}
		if (needComma) {
			os.write(JSONConstants.COMMA,0,1);
		}
		os.write(JSONConstants.DOUBLE_QUOTE,0,1);
		os.write(property);
		os.write(JSONConstants.PROPERTY_END_STRING,0,3);
		if (value) {
			os.write(TRUE,0,4);
		} else {
			os.write(FALSE,0,5);
		}
		os.write(JSONConstants.DOUBLE_QUOTE,0,1);
		return true;
	}
	
	protected boolean writeString(OutputStream os,boolean needComma,byte[] property, String value) throws IOException {
		if (value == null) {
			return needComma;
		}
		if (needComma) {
			os.write(JSONConstants.COMMA,0,1);
		}
		os.write(JSONConstants.DOUBLE_QUOTE,0,1);
		os.write(property);
		os.write(JSONConstants.PROPERTY_END_STRING,0,3);
		encode(value,os);
		os.write(JSONConstants.DOUBLE_QUOTE,0,1);
		return true;
	}
	
	protected boolean writeString(OutputStream os,boolean needComma,byte[] property, byte[] value) throws IOException {
		if (value == null) {
			return needComma;
		}
		if (needComma) {
			os.write(JSONConstants.COMMA,0,1);
		}
		os.write(JSONConstants.DOUBLE_QUOTE,0,1);
		os.write(property);
		os.write(JSONConstants.PROPERTY_END_STRING,0,3);
		os.write(value);
		os.write(JSONConstants.DOUBLE_QUOTE,0,1);
		return true;
	}
	
	protected boolean writeString(OutputStream os,boolean needComma,byte[] property, Enum<?> value) throws IOException {
		if (value == null) {
			return needComma;
		}
		if (needComma) {
			os.write(JSONConstants.COMMA,0,1);
		}
		os.write(JSONConstants.DOUBLE_QUOTE,0,1);
		os.write(property);
		os.write(JSONConstants.PROPERTY_END_STRING,0,3);
		encode(value.name(),os);
		os.write(JSONConstants.DOUBLE_QUOTE,0,1);
		return true;
	}
	
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, byte value) throws IOException {
		return writeNumber(os,needComma,property,String.valueOf(value));
	}
	
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, Byte value) throws IOException {
		if (value == null) {
			return needComma;
		}
		return writeNumber(os,needComma,property,value.toString());
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, short value) throws IOException {
		return writeNumber(os,needComma,property,Short.toString(value));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, Short value) throws IOException {
		if (value == null) {
			return needComma;
		}
		return writeNumber(os,needComma,property,Short.toString(value));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, int value) throws IOException {
		return writeNumber(os,needComma,property,Integer.toString(value));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, Integer value) throws IOException {
		if (value == null) {
			return needComma;
		}
		return writeNumber(os,needComma,property,value.toString());
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, long value) throws IOException {
		return writeNumber(os,needComma,property,Long.toString(value));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, Date value) throws IOException {
		if (value == null) {
			return needComma;
		}
		return writeNumber(os,needComma,property,Long.toString(value.getTime()));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, Calendar value) throws IOException {
		if (value == null) {
			return needComma;
		}
		return writeNumber(os,needComma,property,Long.toString(value.getTimeInMillis()));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, Timestamp value) throws IOException {
		if (value == null) {
			return needComma;
		}
		return writeNumber(os,needComma,property,Long.toString(value.getTime()));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, Long value) throws IOException {
		if (value == null) {
			return needComma;
		}
		return writeNumber(os,needComma,property,value.toString());
	}

	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, float value) throws IOException {
		return writeNumber(os,needComma,property,Float.toString(value));
	}

	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, Float value) throws IOException {
		if (value == null) {
			return needComma;
		}
		return writeNumber(os,needComma,property,Float.toString(value));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, double value) throws IOException {
		return writeNumber(os,needComma,property,Double.toString(value));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma,byte[] property, Double value) throws IOException {
		if (value == null) {
			return needComma;
		}
		return writeNumber(os,needComma,property,Double.toString(value));
	}
	
	protected boolean writeNumber(OutputStream os,boolean needComma, byte[] property,String value) throws IOException {
		if (needComma) {
			os.write(JSONConstants.COMMA,0,1);
		}
		os.write(JSONConstants.DOUBLE_QUOTE,0,1);
		os.write(property);
		os.write(JSONConstants.PROPERTY_END,0,2);
		encode(value,os);
		return true;
	}
	
	// ========================================================================
	
	public static final void encode(String value,OutputStream os) throws IOException {
		char v;
		for (int i = 0 , n = value.length(); i < n ; i++) {
			v = value.charAt(i);
			if(v == 0x0022){//" -> &#34;
				os.write(SKIP_DOUBLE_QUOTE,0,2);
			}else if(v == 0x000A){
				os.write(SKIP_LINE_FEED,0,2);			
			}else if(v == 0x000D){
				continue;
				//SKIP the cariage return
			}else if((v >= 0x0001) && (v <= 0x007F)) {
				os.write((byte)v);
			} else if (v > 0x07FF) {
				os.write((byte) (0xE0 | ((v >> 12) & 0x0F)));
				os.write((byte) (0x80 | ((v >> 6) & 0x3F)));
				os.write((byte) (0x80 | ((v >> 0) & 0x3F)));
			} else {
				os.write((byte) (0xC0 | ((v >> 6) & 0x1F)));
				os.write((byte) (0x80 | ((v >> 0) & 0x3F)));
			}
		}
	}
	
}